2024-12-11

Painless multi-account emailing with imaps and smtp in gnus in emacs

At least it's painless once you know what to do…

So you want to read and write email in emacs and have multiple accounts? And you are not put off by gnus? Then hell do I have an article for you! Make sure you have the login credentials for your accounts ready.

This article is partly based on this one, but with significant improvement.

Let's start with sending mail. The emacs package for that, smtpmail, is a built-in. For a single account, the following configuration may be enough.

(use-package smtpmail
  :custom
    (smtpmail-smtp-server "email.gwdg.de")
    (smtpmail-smtp-service 587))

For multiple accounts, a bit of setup is needed. There are extra dedicated packages for multi-account email, but they are not actually required. We just need to know about X-Message-SMTP-Method. That is a special magic email header, which - when found in a message-mode buffer uppon trying to send the mail - is used to choose the server and authentication used by smtpmail.

This is made useful by message-server-alist. This alist is checked against the "From: "-field of the message you are trying to send (if the first argument is a string, it can also be a function). The second argument is a smtp method string which will be included as that magic header after you try to send the mail.

(use-package message
  :custom
  (message-send-mail-function 'smtpmail-send-it)

  (message-server-alist '(("leonhenrik.plickat@stud.uni-goettingen.de"
                           . "smtp email.gwdg.de 587 ug-student\\leonhenrik.plickat")
                          ("leonhenrik.plickat@uni-goettingen.de"
                           . "smtp email.gwdg.de 587 plickat")
                          ("leonhenrik.plickat@mpinat.mpg.de"
                           . "smtp email.gwdg.de 587 leonhenrik.plickat01"))))

This means you can simply change the "From: "-address in your message-mode buffer to switch the account from which you wish to send the mail. Neat!

To:
Subject:
From: Leon Henrik Plickat <leonhenrik.plickat@stud.uni-goettingen.de> <-- just edit this part to change accounts when sending mail
--text follows this line--

The smtp method string includes the remote address, the port, and the login name. You may be asking yourselves where emacs gets the password from, but I'll get to that later.

Now comes reading mail via imap. Emacs has many mail readers, but I found gnus to be the least frustrating – which says a lot about the other clients because gnus can be infuriatingly frustrating.

(use-package gnus
  :custom
  (gnus-select-method '(nnnil nil))
  (gnus-secondary-select-methods '((nnimap "MPI-Mail"
                                           (nnimap-address "email.gwdg.de")
                                           (nnimap-server-port "imaps")
                                           (nnimap-inbox "INBOX")
                                           (nnimap-user "leonhenrik.plickat01")
                                           (nnimap-stream ssl))
                                   (nnimap "Uni-Mail"
                                           (nnimap-address "email.gwdg.de")
                                           (nnimap-server-port "imaps")
                                           (nnimap-inbox "INBOX")
                                           (nnimap-user "ug-student\\leonhenrik.plickat")
                                           (nnimap-stream ssl))
                                   (nnimap "Work-Mail"
                                           (nnimap-address "email.gwdg.de")
                                           (nnimap-server-port "imaps")
                                           (nnimap-inbox "INBOX")
                                           (nnimap-user "plickat")
                                           (nnimap-stream ssl))))

  (gnus-posting-styles '((".*" ;; By default use my MPI account.
                          (address "Leon Henrik Plickat <leonhenrik.plickat@mpinat.mpg.de>"))
                         ("Work-Mail"
                          (address "Leon Henrik Plickat <leonhenrik.plickat@uni-goettingen.de>"))
                         ("Uni-Mail"
                          (address "Leon Henrik Plickat <leonhenrik.plickat@stud.uni-goettingen.de>")))))

In =gnus-secondary-select-methods you can define your accounts. My setup is a bit weird because I have three accounts on the same mail server and because their login names do not equal the name in the address nor does the domain in the address equal the mail server domain. Check the nnimap documentation should you need anything else, but I think chances are good this will also work for you.

The first argument to nnimap is the internal name of the account. There will be a fun anectdote about that later…

The gnus-posting-styles variable allows you to define an alist which modifies certain values when composing a mail in the context of a gnus "group". Each imaps folder gets a gnus group and the name of the account you defined is included in that name, f.e. like nnimap+MPI-Mail:INBOX. If the first element of an alist entry is a string, it will be interpreted as a regex and matched against group names. Posting styles are incredibly powerful, but I highly doubt that many people will ever use them for anything other than setting the correct address when replying. Then again, not many people will ever use emacs for mail in the first place.

Now, where does emacs get the passwords from?

The standard way is using an authfile. The canonical location for which is ~/.authfile although I strongly advise to use ~/.authfile.gpg instead, which will cause it to be encrypted. Inside the authfile you can specify login details for remote machines, like so:

machine <domain, IP or name of machine> login <user-name> password <password> port imaps
machine <domain, IP or name of machine> login <user-name> password <password> port 587

You will need to add auth configuration for every account. Since the file contains your passwords, I hope you understand why I advise you to encrypt it.

This will work even if you have multiple accounts on the same server like I do, since it also matches against the user name. This is the major reason why the user name is specified in both the gnus account configuration and the automatic smtp configuration for message-mode. If you do not have multiple accounts per mail server, you can likely leave the user name out of the configuration since emacs can just get it from your authfile.

Now, secret management on linux is already quite a mess, so I am not really keen on adding yet another place where passwords live. Luckily, emacs supports other auth-sources as well! If you are on Gnome or KDE, you'll likely want to use the libsecret source, which will integrate with gnome-keyring and kwallet. I myself currently use pass and there is also auth-source integration for it.

(use-package auth-source-pass
  :custom
  (auth-sources '(password-store)))

Unfortunately, the auth integration for pass isn't great. It wants to automatically guess the name of the file the passwords are inside and you can not overwrite this. It checks multiple variations and combinations of the username plus the domain and the port. And, unfortunately, also the name of the account. That's the "fun" anectdote: I have that ugly "-Mail" postfix added to all account names, because otherwise emacs matches the account name against a wrong pass entry which just happens to have a conflicting name. I have the feeling that the person who wrote this code does not actually use it themselves…

I am not really a fan of the default passwordstore convention of the domain plus the username being included in the file names, thereby leaking them; I tend to use more vague keywords for filenames and include the username inside the file using the multi-line feature. However since emacs requires that convention, I added extra files in my passwordstore for all my accounts, like emacs/leonhenrik.plickat01@email.gwdg.de etc., containing just the password (I think emacs/[<user-name>@]<domain>[:<port>] is the most reasonable template when using pass as an emacs auth-source). If only emacs allowed to simply specifiy a command to run to get the password, but I guess we can't have nice things 🐈

If you have trouble with the authentication failing and want to debug it, then set auth-source-debug to 'trivia and check the messages buffer. It should tell you which combination of account-name, user-name, remote address and port it tried to match against which auth-source. This is how I discovered my bad auth match.

Of course, also check the documentation.

Anyway, you should now be able to read mails via gnus and compose messages both from inside gnus and via C-x m. I hope this article saves someone else the hours I put into this.

I do wonder how hard it would be to create my own custom auth-source to simplify this a bit. Perhaps one allowing to specify commands to run.

Articles from blogs I read (generated by openring)

whippet lab notebook: guile, heuristics, and heap growth

Greets all! Another brief note today. I have gotten Guile working with one of the Nofl-based collectors, specifically the one that scans all edges conservatively (heap-conservative-mmc / heap-conservative-parallel-mmc). Hurrah!It was a pleasant surprise h…

wingolog, May 22, 2025

Status update, May 2025

Hi! Today wlroots 0.19.0 has finally been released! Among the newly supported protocols, color-management-v1 lays the first stone of HDR support (backend and renderer bits are still being reviewed) and ext-image-copy-capture-v1 enhances the previous screen ca…

emersion, May 15, 2025

Summary of changes for April 2025

Hey everyone!This is the list of all the changes we've done to our projects during the month of April. 100r.co, updated water, ditch bag, woodstove installation, and added new photos and information on first-aid kit. Rabbit Waves, updated Triangular…

Hundred Rabbits, April 30, 2025